import axios from 'axios'
import cache from './cache'
import config from './config'
import store from '@/store'
import { Toast, Notify } from 'vant';
import Router from '@/router'
import {VM} from '@/main'
import { user } from '@/api'

Toast.allowMultiple();
const DOMAIN = config.url

const request = axios.create({
  baseURL: DOMAIN,
  timeout: 30000
})

export const SUCCESS = 1
const api = {}
api.domain = DOMAIN
//设定成功时候的rst值
api.SUCCESS = SUCCESS
//设定未登录时的rst返回值
api.UnAuthorized = -1
const HttpMethod = {GET:'get', POST:'post', DEL:'delete', PUT:'put', PATCH:'patch'}

// 配置缓存请求api,用于缓存get和post请求数据,
const cacheRequest = cache(request, {
  cacheTime: 5, //缓存周期 默认是5s 单位秒
  MaxCacheLen: 1000 //最多缓存接口数 默认是1000条
})

// 1、防止重复请求； 2、当页面离开时，取消当前页未完成的网络请求
const cancelToken = axios.CancelToken
let pending = [] //用于存储每个ajax请求的取消函数和ajax标识
const getUrlKey = function(ever){ //依据参数获取请求唯一性key值
  return `${ever.url}?method=${ever.method}&data=${JSON.stringify(ever.method=='get'?ever.params:ever.data)}`
}
/* @isroute 取消请求的判断依据
false 依据getUrlKey
true 依据路由变动
 */
export const removePending = (ever, isroute=false) => {
    for(let p in pending){
        if(isroute?(pending[p].r == ever && pending[p].c):(pending[p].u == getUrlKey(ever))) { //当前请求在数组中存在时执行函数体
          pending[p].f() //执行取消操作
          pending.splice(p, 1,'') //把这条记录从数组中移除
        }
    }
    pending = pending.filter(item=>item!='')
}

// 添加请求拦截器
request.interceptors.request.use(function (config) {
  var apiToken = store.state.user.api_token
  var lang = store.state.user.lang
  apiToken && (config.headers.common['Authorization'] = 'Bearer ' + apiToken)
  config.headers.common['uuid'] = user.uuid
  config.headers.common['lang'] = lang
  // 在发送请求之前做些什么
  //暂存当前请求信息，并设置请求取消函数
  removePending(config)
  config.cancelToken = new cancelToken((c)=>{
      pending.push({ u: getUrlKey(config), r:VM.$route.name, c:config.cancel, f: c })
  })

  // 转换为 FormData 传参
  const data = config.data
  if (config.method === 'post' && data) {
    const formData = new FormData()
    for (const key of Object.keys(data)) {
      formData.append(key, data[key])
    }
    config.data = formData
  }

  return config
}, function (error) {
  // 对请求错误做些什么
  return Promise.reject(error)
})

// 添加响应拦截器
request.interceptors.response.use(function (response) {
  removePending(response.config)  //从pending移除已经完成的请求
  if (typeof response.data == 'string') {
    let error = {response: response}
    return Promise.reject(error)
  }
  if (response.data && response.data.rst == api.UnAuthorized) {
    // 用户不存在，退出登录
    store.dispatch('user/setApiToken',null)
    // store.dispatch('user/setUserInfo',null)
    // Router.replace({name:'home', params:{active:0}})
    Router.replace({ name: 'Login' })
    return null
  }
  // 对响应数据做点什么
  return response
}, function (error) {
  // 对响应错误做点什么
  return Promise.reject(error)
})

const get = function (url,
  {
    data={},
    showLoading=false,
    showError=true,
    cache=true,
    cancel=true
  }={}) {
  return requestHttp(url, HttpMethod.GET, data, showLoading, showError, cache, cancel)
}
api.get = get

const post = function (url,
  {
    data={},
    showLoading=false,
    showError=true,
    cache=false,
    cancel=true
  }={}) {
  return requestHttp(url, HttpMethod.POST, data, showLoading, showError, cache, cancel)
}
api.post = post

const del = function (url,
  {
    data={},
    showLoading=false,
    showError=true,
    cache=false,
    cancel=true
  }={}) {
  return requestHttp(url, HttpMethod.DEL, data, showLoading, showError, cache, cancel)
}
api.del = del

const put = function (url,
  {
    data={},
    showLoading=false,
    showError=true,
    cache=false,
    cancel=true
  }={}) {
  return requestHttp(url, HttpMethod.PUT, data, showLoading, showError, cache, cancel)
}
api.put = put

const patch = function (url,
  {
    data={},
    showLoading=false,
    showError=true,
    cache=false,
    cancel=true
  }={}) {
  return requestHttp(url, HttpMethod.PATCH, data, showLoading, showError, cache, cancel)
}
api.patch = patch



/* 封装网络请求函数，包括加载框显示，错误处理，缓存配置, 是否在离开当前页面路由时，取消此页面所有未完成的数据请求
@url 接口地址
@method 请求方法
@data 请求数据
@showLoading Boolean 显示加载框
@showError Boolean 是否在加载数据错误时显示错误提示
@cache Boolean | Number 缓存设置，true为启用默认缓存时间， Number为自定义缓存时间
@cancel Boolean 是否在离开当前页面路由时，取消此页面所有未完成的数据请求
 */
const requestHttp = function (url, method, data, showLoading, showError, cache, cancel) {
  let httpClient = cache ? cacheRequest : request // 判断缓存cache，为true时启用默认缓存时间，为Number时为自定义缓存时间单位秒
  let loadToast;
  if(showLoading){
    loadToast = Toast.loading({
      message: $i18n.t('加载中'),
      forbidClick: true,
      duration:0
    });
  }

  return httpClient({url, [method==HttpMethod.GET?'params':'data']:data, method, cache, cancel}).then(res=>{
    let data = res && res.data || {msg:'USER_NOT_EXIST'}
    //数据请求错误处理
    if(data.rst != api.SUCCESS){
      let _msg = data.msg && data.msg.length < 500 ? data.msg : '数据错误'
      data.msg = _msg
      Toast.clear(true)
      showError && data.msg && Toast($i18n.t(`error_code.${data.msg}`).replace('error_code.',''));
    }
    return data
  }).catch(error=>{
    //网络请求错误处理
    let errorResponse = {path:'', rst:'', code:'', msg:'', data:null}
    if(error.response){
      errorResponse.path = error.response.config.url
      errorResponse.code = error.response.status
      errorResponse.msg = error.response.data.constructor==String?(error.response.data.length < 500 ? error.response.data : '网络请求错误'):error.response.data.error||error.response.data.msg || '网络请求错误'
    } else {
      errorResponse.msg = '无法连接网络'
    }
    //401状态退出登录
    if(errorResponse.code==api.UnAuthorized){
      store.dispatch('user/setApiToken',null)
      // store.dispatch('user/setUserInfo',null)
      // Router.replace({name:'home', params:{active:0}})
      Router.replace({ name: 'Login' })
      return
    }
    //主动取消请求，不提示错误
    if (!axios.isCancel(error)) {
      Notify.clear()
      Notify({
        message: errorResponse.path?`path: "${errorResponse.path}"\n${errorResponse.code} ${$i18n.t('error_code.'+errorResponse.msg)}`:`${errorResponse.code} ${$i18n.t('error_code.'+errorResponse.msg)}`,
        duration: 4000,
        className:`custom ${window['cordova'] && config.invasion ? 'invasion':''}`
      });
    }
    return Promise.resolve(errorResponse)
  }).finally(()=>{
    loadToast && loadToast.clear()
  })

}


export default api
